﻿using System;
using System.Collections.Generic;
using System.Linq;

using DataModel.Models;
using DataModel.Managers.SubManage;

namespace DataModel.Managers
{
    public class ScheduleManager
    {
        private readonly UniversityPortalEntities _entityModel;

        public ScheduleManager()
        {
            _entityModel = new UniversityPortalEntities();
        }

        public ScheduleManager(UniversityPortalEntities entityModel)
        {
            _entityModel = entityModel;
        }

        #region GetInformationForSchedule

        /// <summary>
        /// get lessons by group
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <returns></returns>
        public IQueryable<Lesson> GetLessonsByGroup(Int64 groupNumber)
        {
            return from l in _entityModel.Lessons
                   where l.Groups.FirstOrDefault(g => g.number == groupNumber) != null
                   orderby l.beginTime
                   select l;
        }

        /// <summary>
        /// Get lessons by group in semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public IQueryable<Lesson> GetLessonsByGroup(Int64 groupNumber, int semester)
        {
            return from l in _entityModel.Lessons
                   where (l.semester == semester && l.Groups.FirstOrDefault(g => g.number == groupNumber) != null)
                   orderby l.beginTime
                   select l;
        }

        /// <summary>
        /// Get All Subject by Group
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <returns></returns>
        public IQueryable<Subject> GetSubjectsByGroup(Int64 groupNumber)
        {
            var currentGroupLessons = this.GetLessonsByGroup(groupNumber);

            return from s in _entityModel.Subjects
                   from l in currentGroupLessons
                   where s.ID == l.subjectID
                   select s;
        }

        /// <summary>
        /// Get subject by group in semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public IQueryable<Subject> GetSubjectsByGroup(Int64 groupNumber, int semester)
        {
            var currentGroupLessons = this.GetLessonsByGroup(groupNumber, semester);

            return from s in _entityModel.Subjects
                   from l in currentGroupLessons
                   where s.ID == l.subjectID
                   select s;
        }

        /// <summary>
        /// Get Schedule by group on DateBegin to DateEnd
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="fromDate"></param>
        /// <param name="toDate"></param>
        /// <returns></returns>
        public ScheduleModel GetScheduleByGroup(Int64 groupNumber, DateTime fromDate, DateTime toDate)
        {
            var scheduleModel = new ScheduleModel(new List<DayModel>()) {GroupNumber = groupNumber};

            var saveTriggerByDay = false;

            // TODO: optimize queries to get lessons per date
            var lessons = GetLessonsByGroup(groupNumber);
            while (fromDate <= toDate)
            {
                var dayModel = new DayModel(new List<LessonModel>(), fromDate);

                foreach (var lesson in lessons)
                {
                    if (LessonInDate(fromDate, lesson))
                    {
                        var lm = new LessonModel
                            {
                                LessonID = lesson.ID,
                                BeginTime = new DateTime(lesson.beginTime.Ticks),
                                EndTime = new DateTime(lesson.endTime.Ticks),
                                Subject = lesson.Subject,
                                Professor = lesson.Professor,
                                Auditory = lesson.Auditory,
                                LessonType = lesson.LessonType
                            };
                        dayModel.lessonModels.Add(lm);
                        saveTriggerByDay = true;
                    }
                }

                if (saveTriggerByDay)
                {
                    scheduleModel.dayModels.Add(dayModel);
                }

                fromDate = fromDate.AddDays(1);
                saveTriggerByDay = false;
            }

            return scheduleModel;
        }

        /// <summary>
        /// Get Schedule By group in sem
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="semester"></param>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        public ScheduleModel GetScheduleByGroup(Int64 groupNumber, int semester, DateTime begin, DateTime end)
        {
            var lessons = this.GetLessonsByGroup(groupNumber, semester);
            ScheduleModel scheduleModel = new ScheduleModel(new List<DayModel>());
            scheduleModel.GroupNumber = groupNumber;

            bool saveTriggerByDay = false;

            while (begin <= end)
            {
                var dayModel = new DayModel(new List<LessonModel>(), begin);

                foreach (var lesson in lessons)
                {
                    if (LessonInDate(begin, lesson))
                    {
                        LessonModel lm = new LessonModel();
                        lm.LessonID = lesson.ID;
                        lm.BeginTime = new DateTime(lesson.beginTime.Ticks);
                        lm.EndTime = new DateTime(lesson.endTime.Ticks);
                        lm.Subject = lesson.Subject;
                        lm.Professor = lesson.Professor;
                        lm.Auditory = lesson.Auditory;
                        lm.LessonType = lesson.LessonType;
                        dayModel.lessonModels.Add(lm);
                        saveTriggerByDay = true;
                    }
                }

                if (saveTriggerByDay == true) scheduleModel.dayModels.Add(dayModel);

                begin = begin.AddDays(1);
                saveTriggerByDay = false;
            }

            return scheduleModel;
        }

        public ScheduleModel GetSemesterShchedule(Int64 groupNumber, int semester)
        {
            var schedule = new ScheduleModel(new List<DayModel>());
            schedule.GroupNumber = groupNumber;

            var lessons = this.GetLessonsByGroup(groupNumber, semester);
            var lessonModels = new List<LessonModel>();
            foreach (var lesson in lessons)
            {
                var lm = new LessonModel
                             {
                                 LessonID = lesson.ID,
                                 BeginTime = new DateTime(lesson.beginTime.Ticks),
                                 EndTime = new DateTime(lesson.endTime.Ticks),
                                 Subject = lesson.Subject,
                                 Professor = lesson.Professor,
                                 Auditory = lesson.Auditory,
                                 LessonType = lesson.LessonType,
                                 BeginDate = lesson.beginDate,
                                 EndDate = lesson.endDate,
                                 PeriodicityMask = lesson.periodicityMask,
                                 Periodicity = lesson.periodicity,
                                 Semester = lesson.semester,
                                 WeekDay = lesson.weekDay
                             };

                lessonModels.Add(lm);
            }

            for (var i = 0; i < GetStudyDayCount(lessonModels); i++)
            {
                schedule.dayModels.Add(new DayModel(new List<LessonModel>(), DateTime.Now));
            }

            foreach (var lessonModel in lessonModels)
            {
                schedule.dayModels[lessonModel.WeekDay - 1].lessonModels.Add(lessonModel);
            }

            

            return schedule;
        }

        private int GetStudyDayCount(IEnumerable<LessonModel> list)
        {
            var count = 0;
            foreach (var item in list)
            {
                if (item.WeekDay > count)
                {
                    count = item.WeekDay;
                }
            }
            return count;
        }


        /// <summary>
        /// Hard Code
        /// </summary>
        /// <param name="date"></param>
        /// <param name="lesson"></param>
        /// <returns></returns>
        #endregion

        public static bool LessonInDate(DateTime date, Lesson lesson)
        {
            if (lesson.weekDay != date.DayOfWeek.ParseDayOfWeek() || (lesson.beginDate > date) || (date > lesson.endDate))
                return false;

            if (lesson.periodicity == 0)
            {
                return true;
            }

            int bitOfMask = (WeekHelper.GetStudyWeekNumber(date) % lesson.periodicity) - 1;
            int testParam = 1 << bitOfMask;

            return ((lesson.periodicityMask & testParam) != 0);
        }

        #region EditSchedule

        public void ChangeLessonData(Lesson lesson)
        {
            Lesson changingLesson = _entityModel.Lessons.FirstOrDefault(l => l.ID == lesson.ID);
            if (changingLesson == null) throw new NullReferenceException("Changing lesson is not found");

            //shallow copy of object's changes
            changingLesson.Auditory = lesson.Auditory;
            changingLesson.auditoryID = lesson.auditoryID;
            changingLesson.AuditoryReference = lesson.AuditoryReference;
            changingLesson.beginDate = lesson.beginDate;
            changingLesson.beginTime = lesson.beginTime;
            changingLesson.endDate = lesson.endDate;
            changingLesson.endTime = lesson.endTime;
            changingLesson.EntityKey = lesson.EntityKey;
            changingLesson.Groups = lesson.Groups;
            changingLesson.JournalCells = lesson.JournalCells;
            changingLesson.LessonType = lesson.LessonType;
            changingLesson.lessonTypeID = lesson.lessonTypeID;
            changingLesson.LessonTypeReference = lesson.LessonTypeReference;
            changingLesson.periodicity = lesson.periodicity;
            changingLesson.periodicityMask = lesson.periodicityMask;
            changingLesson.Professor = lesson.Professor;
            changingLesson.professorID = lesson.professorID;
            changingLesson.ProfessorReference = lesson.ProfessorReference;
            changingLesson.semester = lesson.semester;
            changingLesson.Subject = lesson.Subject;
            changingLesson.subjectID = lesson.subjectID;
            changingLesson.SubjectReference = lesson.SubjectReference;
            changingLesson.weekDay = lesson.weekDay;
            //Save Changes in database
            _entityModel.SaveChanges();
        }


        #endregion
    }
}